ACLOCAL_AMFLAGS = -I build-aux
AM_DISTCHECK_CONFIGURE_FLAGS = --enable-developer-mode --without-developer-tools PDFINFO=$(PDFINFO)

.ONESHELL:
.SECONDARY:
.SECONDEXPANSION:
.DELETE_ON_ERROR:

SUBDIRS =
if !SYSTEM_LIBTEXPDF
SUBDIRS += libtexpdf
endif
SUBDIRS += justenough .

docdir = $(datarootdir)/doc/$(TRANSFORMED_PACKAGE_NAME)
licensedir = $(datarootdir)/licenses/$(TRANSFORMED_PACKAGE_NAME)
datadir = $(datarootdir)/$(TRANSFORMED_PACKAGE_NAME)
pkgdatadir = $(datadir)

# Rules for installing LuaRocks locally as pkgdata_DATA
include Makefile-luarocks

# Actual rules for downloading test fonts are in a separate file
include Makefile-fonts

# Our regression test suite includes test for all features. Anything that is
# only expected to work with code behind a feature flag needs to be filtered
# out if we are configured with the feature disabled.
_DISABLEDSRCS =
if !FONT_VARIATIONS
_DISABLEDSRCS += tests/variations-%
endif
export DISABLEDSRCS ?= $(filter $(_DISABLEDSRCS),$(_TESTSRCS))
TESTSRCS ?= $(filter-out $(DISABLEDSRCS),$(_TESTSRCS))
TESTPREVIEWS ?= $(addsuffix .pdf,$(basename $(filter-out $(_DISABLEDSRCS),$(_TESTPREVIEWS))))

# Since we can't use $(wildcard ...) while defining file lists automake, we
# generate a file with a script that builds our dynamic file lists ahead of
# time and stores them in a variable. Using an include here is tricky, because
# if we just include the file directly automake will flatten it. By using
# $(wildcard ...) to include it (the very function we couldn't use where we
# wanted to in the first place) we keep the include from being flattened and
# hence evaluated where we want it to be. Otherwise the flattening gets the
# variables set in all the wrong order. Since the file will be generated by
# a make (see BUILT_SOURCES) before make dist the source will exist in time.
include $(wildcard $(top_srcdir)/Makefile-distfiles)

FIGURES = documentation/fig-input-to-output.pdf
MANUAL := documentation/sile.pdf
SILELUA := $(PACKAGE_NAME)-lua

if MANUAL
_MANUAL = $(MANUAL)
endif

$(MANUAL): $(FIGURES)

BUILT_SOURCES_LUA = core/features.lua core/pathsetup.lua core/version.lua

bin_PROGRAMS = sile
bin_SCRIPTS = sile-lua
pkglib_LIBRARIES =
nodist_man_MANS =
dist_man_MANS = sile-lua.1
sile_SOURCES = src/bin/sile.rs src/lib.rs src/cli.rs src/types.rs src/types/semver.rs
EXTRA_sile_SOURCES =
if !EMBEDDED_RESOURCES
nobase_dist_pkgdata_DATA = $(SILEDATA) $(LUALIBRARIES)
nobase_nodist_pkgdata_DATA = $(BUILT_SOURCES_LUA) $(LUAMODULES)
endif !EMBEDDED_RESOURCES
dist_doc_DATA = README.md CHANGELOG.md
dist_pdf_DATA = $(_MANUAL)
dist_license_DATA = LICENSE.md
EXTRA_DIST = spec tests documentation sile-dev-1.rockspec rusile-dev-1.rockspec fontconfig.conf
EXTRA_DIST += build-aux/action-updater.js build-aux/cargo-updater.js build-aux/config.ld build-aux/decore-automake.sh build-aux/git-version-gen
EXTRA_DIST += Dockerfile bootstrap.sh build-aux/docker-bootstrap.sh build-aux/docker-fontconfig.conf hooks/build
EXTRA_DIST += build-aux/xml-entities-to-lua.xsl
EXTRA_DIST += default.nix flake.nix flake.lock shell.nix build-aux/pkg.nix
EXTRA_DIST += package.json # imported by both Nix and Docker
EXTRA_DIST += $(FIGURES)
EXTRA_DIST += src/embed.rs.in
EXTRA_DIST += sil.abnf

if EMBEDDED_RESOURCES
EXTRA_DIST += $(SILEDATA) $(LUALIBRARIES)
endif

BUILT_SOURCES = $(BUILT_SOURCES_LUA) Makefile-distfiles

if SHARED
EXTRA_RUNTIME_DEPS = $(pkglib_LIBRARIES)
else !SHARED
EXTRA_RUNTIME_DEPS =
endif

MATHML_ENTITIES = packages/math/mathml-entities.lua
EXTRA_DIST += $(MATHML_ENTITIES)
BUILT_SOURCES += $(MATHML_ENTITIES)

CLEANFILES = $(MANUAL)

DISTCLEANFILES = @AMINCLUDE@

# The boilerplate Docker is setup as if GHCR was our primary, and the CI jobs also
# manually override these values. But as long as we're manually running a sync of
# the auto deployed GHCR emiages to Docker Hub, the manual invocations need extra help.
export GHCR_REGISTRY ?= ghcr.io
export GHCR_REPO ?= sile-typesetter/$(TRANSFORMED_PACKAGE_NAME)
export DOCKER_REGISTRY ?= docker.io
export DOCKER_REPO ?= siletypesetter/$(TRANSFORMED_PACKAGE_NAME)
export DOCKER_TAG ?= HEAD

# A classical use of the autoconf-archive include macro would expand
# INC_AMINCLUDE here, but the perl script that inlines include statements
# runs before the automake that organizes logic and performs substitution.
# Consequentially with a substitution here it becomes impossible to use
# automake conditionals and substitutions in the included Makefile fragments.
# By entering the expanded value directly we are ready in time for the inlining
# functionality and hence can use conditionals in included makefile fragments.
include $(top_srcdir)/aminclude.am

Makefile-distfiles: $(wildcard .version .tarball-version) | $(LUAMODLOCK)
	$(SHELL) build-aux/list-distfiles.sh > $@

if EMBEDDED_RESOURCES
_EMBEDDED_SOURCES = src/embed.rs src/embed-includes.rs
nodist_sile_SOURCES = $(_EMBEDDED_SOURCES)
BUILT_SOURCES += $(_EMBEDDED_SOURCES)
CLEANFILES += $(_EMBEDDED_SOURCES)
$(CARGO_BIN): justenough/.libs/fontmetrics.a
$(CARGO_BIN): justenough/.libs/justenoughfontconfig.a
$(CARGO_BIN): justenough/.libs/justenoughharfbuzz.a
$(CARGO_BIN): justenough/.libs/justenoughicu.a
$(CARGO_BIN): justenough/.libs/justenoughlibtexpdf.a
$(CARGO_BIN): justenough/.libs/svg.a
$(CARGO_BIN): libtexpdf/.libs/libtexpdf.a

src/embed-includes.rs: Makefile-distfiles
	{
		echo $(BUILT_SOURCES_LUA)
		$(SED) -ze 's/\\\n\t//g' $< |
			$(GREP) -E '^(SILEDATA|LUALIBRARIES|LUAMODULES) = '
	} |
		$(SED) -E -e 's/^.* = //;s/ /\n/g' |
		while read file; do
			echo "#[include = \"$${file}\"]"
		done > $@

src/embed.rs: src/embed.rs.in src/embed-includes.rs
	$(SED) \
		-e '/@EMBEDDED_INCLUDE_LIST@/r $(word 2,$^)' \
		-e '/@EMBEDDED_INCLUDE_LIST@/d' \
		$< > $@

else !EMBEDDED_RESOURCES
$(CARGO_BIN): $(pkglib_LIBRARIES)
endif !EMBEDDED_RESOURCES

if LUAJIT
MLUAVER = luajit
else
MLUAVER = lua$(LUA_SHORT_VERSION)
endif
CARGO_FEATURE_ARGS = --features $(MLUAVER)
rusile_FEATURE_ARGS = --features $(MLUAVER)

if !SYSTEM_LUA_SOURCES
CARGO_FEATURE_ARGS += --features vendored
endif

if EMBEDDED_RESOURCES
CARGO_FEATURE_ARGS += --features static
endif

if FONT_VARIATIONS
CARGO_FEATURE_ARGS += --features variations
endif

DEPDIR := .deps
LOCALFONTS := FONTCONFIG_FILE=$(PWD)/fontconfig.conf
LOCALPATHS := SILE_PATH="$(PWD);libtexpdf/.libs;justenough/.libs"
SILEFLAGS ?= -m $(DEPDIR)/$(basename $@).d -d versions -f fontconfig
DRAFT ?= false

dist-hook: dist-hook-decore-automake dist-hook-devendor-luarocks

.PHONY: dist-hook-decore-automake
dist-hook-decore-automake:
	cd $(distdir)
	$(top_srcdir)/build-aux/decore-automake.sh

# Don't take it for granted that the user will use --without-system-luarocks
# when building from source tarballs even though the dist tarball may be
# generated with that configuration.
.PHONY: dist-hook-devendor-luarocks
dist-hook-devendor-luarocks: dist-hook-decore-automake
	cd $(distdir)
	$(SED) -i -e '/^LUAMODULES/d;/^\tlua_modules/d' Makefile.in

# Whether to force tests to run from scratch
CLEAN ?=

dist: sile-$(VERSION).pdf sile-$(VERSION).md

dist-local: $(BUILT_SOURCES)

sile-$(VERSION).pdf: $(MANUAL)
	$(INSTALL) $(MANUAL) $@

sile-%.md: CHANGELOG.md
	$(SED) -e '/\.\.\.v$*/,/\.\.\.v/!d' CHANGELOG.md | \
		$(SED) -e '1,3d;N;$$!P;$$!D;$$d' |
	$(if $(TYPOS),$(TYPOS) --write-changes,cat) - > $@

check: selfcheck

.PHONY: selfcheck
selfcheck: | $(bin_PROGRAMS) $(_BUILT_SUBDIRS) $(EXTRA_RUNTIME_DEPS)
	output=$$(mktemp -t selfcheck-XXXXXX.pdf)
	trap '$(RM) $$output' EXIT HUP TERM
	echo "<sile>foo</sile>" | $(LOCALPATHS) ./$(bin_PROGRAMS) -o $$output -
	$(PDFINFO) $$output | $(GREP) "SILE v$(VERSION)"

.PHONY: docs
docs: $(_MANUAL) lua-api-docs rust-api-docs

.PHONY: fake-manual
fake-manual:
	touch $(FIGURES)
	touch $(MANUAL)

.PHONY: lua-api-docs
lua-api-docs: lua-api-docs/index.html

lua-api-docs/index.html: build-aux/config.ld
	$(LDOC) -c build-aux/config.ld .

.PHONY: rust-api-docs
rust-api-docs: rust-api-docs/doc/sile/index.html

rust-api-docs/doc/sile/index.html:
	cargo doc $(CARGO_FEATURE_ARGS) --no-deps --target-dir rust-api-docs

.PHONY: docs-figures
docs-figures: $(FIGURES)

# This is a monkey patch to figure out how many passes we have to to to
# guarantee the TOC is up to date, simplify when #230 is fixed.
hastoc = [ -f $(subst .pdf,.toc,$@) ] && echo true || echo false
pages = $(PDFINFO) $@ | $(AWK) '$$1 == "Pages:" {print $$2}' || echo 0
localsile = $(LOCALFONTS) $(LOCALPATHS) ./$(bin_PROGRAMS) $(SILEFLAGS)
silepass = $(localsile) $< -o $@ && pg0=$${pg} pg=$$($(pages)) || false
define runsile =
	set -e
	pg=$$($(pages)) hadtoc=$$($(hastoc))
	$(MKDIR_P) $(DEPDIR)/$$(dirname $@)
	$(silepass)
	export -n SILE_COVERAGE
	if $$($(hastoc)) && ! $(DRAFT); then
		$${hadtoc} || $(silepass)
		[ "$${pg}" = "$${pg0}" ] || $(silepass)
	fi
endef

_FORCED = $(and $(SILE_COVERAGE)$(CLEAN),force)
_TEST_DEPS = $(and $$(filter tests/%,$@),$(addprefix .fonts/,$(TESTFONTFILES)))
_DOCS_DEPS = $(and $$(filter documentation/%,$@),$(addprefix .fonts/,$(DOCSFONTFILES)))

_SUBDIR_TELLS =

if SHARED
_SUBDIR_TELLS += justenough/.libs/fontmetrics$(LIBEXT) \
				justenough/.libs/justenoughfontconfig$(LIBEXT) \
				justenough/.libs/justenoughharfbuzz$(LIBEXT) \
				justenough/.libs/justenoughicu$(LIBEXT) \
				justenough/.libs/justenoughlibtexpdf$(LIBEXT) \
				justenough/.libs/svg$(LIBEXT) \
				libtexpdf/.libs/libtexpdf$(LIBEXT).0.0.0
endif

if STATIC
_SUBDIR_TELLS += justenough/.libs/fontmetrics.a \
				justenough/.libs/justenoughfontconfig.a \
				justenough/.libs/justenoughharfbuzz.a \
				justenough/.libs/justenoughicu.a \
				justenough/.libs/justenoughlibtexpdf.a \
				justenough/.libs/svg.a \
				libtexpdf/.libs/libtexpdf.a
endif

$(_BUILT_SUBDIRS): $(_SUBDIR_TELLS)
	touch $@

CLEANFILES += $(_BUILT_SUBDIRS)

$(_SUBDIR_TELLS):
	$(MAKE) $(AM_MAKEFLAGS) all-recursive

patterndeps = $(_FORCED) $(_TEST_DEPS) $(_DOCS_DEPS) | $(bin_PROGRAMS) $(EXTRA_RUNTIME_DEPS) $(DEPDIRS) $(LUAMODLOCK) $(_BUILT_SUBDIRS)

%.pdf: %.sil $$(patterndeps)
	$(runsile)

%.pdf: %.xml $$(patterndeps)
	$(runsile)

%.pdf: %.nil $$(patterndeps)
	$(runsile)

%.pdf: %.dot
	$(DOT) -Tpdf $< -o $@.gs
	$(GS) -q -sDEVICE=pdfwrite -dCompatibilityLevel=1.5 -o $@ $@.gs

XML_ENTITIES = .sources/unicode.xml
XML_ENTITIES_COMMIT = 77acf14428202e4e1dba54ff1e5ed43fe5ab474f

$(XML_ENTITIES):
	$(CURL) https://raw.githubusercontent.com/w3c/xml-entities/$(XML_ENTITIES_COMMIT)/unicode.xml -o $@

$(MATHML_ENTITIES): build-aux/xml-entities-to-lua.xsl
	$(XSLTPROC) $< $(XML_ENTITIES) | $(or $(STYLUA),cat) - > $@

.PHONY: force
force: ;

PHONY_DEVELOPER_TARGETS = busted compare coverage \
	docker-dep-check docker-ghcr-to-hub docker-test-dist gource.webm lint luacheck luarocks-lint \
	prerelease regression_previews regressions release release-preview stylua typos tagrelease \
	test update_expecteds update_libtexpdf vendored-crates
.PHONY: $(PHONY_DEVELOPER_TARGETS)

if DEVELOPER_MODE

RELTYPE ?=

tagrelease:
	test -z $$($(GIT) tag --points-at HEAD) || exit 0 # end if we are already on a release tag
	$(GIT) diff-index --quiet --cached HEAD || exit 1 # die if anything staged but not committed
	$(GIT) diff-files --quiet || exit 1 # die if any tracked files have unstagged changes
	$(NPM) run release -- --preMajor $(and $(RELTYPE),--release-as $(RELTYPE))

prerelease: test docs update_libtexpdf

release-preview:
	$(NPM) run release -- --preMajor --dry-run $(and $(RELTYPE),--release-as $(RELTYPE))

release: tagrelease

update_libtexpdf:
	$(GIT) diff-index --quiet --cached HEAD || exit 1 # die if anything already staged
	$(GIT) submodule update --init --remote -- libtexpdf
	$(NIX) flake lock --override-input libtexpdf-src github:sile-typesetter/libtexpdf/$(shell $(GIT) submodule status -- libtexpdf | awk '{print $$1}')
	$(GIT) add -- libtexpdf flake.lock
	$(GIT) diff-index --quiet --cached HEAD || $(GIT) commit -m "chore(build): Pin latest libtexpdf library submodule"

TESTPDFS   = $(addsuffix      .pdf,$(basename $(TESTSRCS)))
EXPECTEDS ?= $(filter $(addsuffix .expected,$(basename $(TESTSRCS))),$(TESTEXPECTS))
ACTUALS    = $(addsuffix   .actual,$(basename $(EXPECTEDS)))
CLEANFILES += $(TESTPDFS) $(ACTUALS)

REGRESSIONSCRIPT := ./tests/regressions.pl
BUSTEDFLAGS ?=

regressions: $(TESTSRCS) $(ACTUALS)
	$(LOCALFONTS) $(REGRESSIONSCRIPT) $(TESTSRCS)

check-local: regressions busted

lint: luacheck luarocks-lint stylua typos

luarocks-lint: $(LUAMODSPEC)
	$(LUAROCKS) lint $(LUAMODSPEC)

luacheck:
	$(LUACHECK) -j$(shell nproc) -q .

stylua:
	$(STYLUA) --check --respect-ignores -g '*.lua' -g '*.lua.in' -g '*.rockspec.in' sile.in .busted .luacov .luacheckrc build-aux/config.ld .

typos:
	$(TYPOS) .

busted: $(SILELUA) $(addprefix .fonts/,$(TESTFONTFILES)) $(BUSTEDSPECS)
	set -f; IFS=';'
	packagepath=(./{,lua-libraries/}?{,/init}.lua)
	packagecpath=(./{,core/,{libtexpdf,justenough}/.libs/}?$(LIBEXT))
if !SYSTEM_LUAROCKS
	packagepath+=(./lua_modules/share/lua/$(LUA_VERSION)/?{,/init}.lua)
	packagecpath+=(./lua_modules/lib/lua/$(LUA_VERSION)/?$(LIBEXT))
endif
# Note: Busted tests can't run with out static build since the Rusile module is
# only available embedded into it. We can test on the dynamic builds where it
# is available as a use space module. This could change if we enable a lua vm
# pass through mode and use that as busted's interpreter.
if SHARED
# Note: use of --lua causes this to be passed back through a shell loosing one
# layer of quoting. Drop single quotes if removing.
	$(LOCALFONTS) $(BUSTED) --lua=$(LUA) --lpath="'$${packagepath[*]};;'" --cpath="'$${packagecpath[*]};;'" $(BUSTEDFLAGS) .
endif

coverage: export SILE_COVERAGE=1
coverage: BUSTEDFLAGS += -c
coverage: regression_previews busted

HEADSHA ?= HEAD
_HEADSHA ?= $(shell test -e .git && $(GIT) rev-parse --short=7 $(HEADSHA))
BASESHA ?= $(HEADSHA)^
_BASESHA ?= $(shell test -e .git && $(GIT) rev-parse --short=7 $(BASESHA))

clean-recursive: clean-tests

clean-tests:
	$(RM) -r tests/*.actual
	$(RM) -r $(DEPDIR)/tests/*

clean-recursive: clean-deps

clean-deps:
	$(RM) -r $(DEPDIR)

update_expecteds: $(EXPECTEDS)

tests/%.expected: tests/%.sil $$(patterndeps)
	$(localsile) -b debug $< -o $@

tests/%.expected: tests/%.xml $$(patterndeps)
	$(localsile) -b debug $< -o $@

tests/%.expected: tests/%.nil $$(patterndeps)
	$(localsile) -b debug $< -o $@

regression_previews: $(TESTPREVIEWS)

tests/%.actual: tests/%.sil $$(patterndeps)
	-$(if $(CLEAN),$(RM) $@,:)
	$(localsile) -b debug $< -o $@

tests/%.actual: tests/%.xml $$(patterndeps)
	-$(if $(CLEAN),$(RM) $@,:)
	$(localsile) -b debug $< -o $@

tests/%.actual: tests/%.nil $$(patterndeps)
	-$(if $(CLEAN),$(RM) $@,:)
	$(localsile) -b debug $< -o $@

DEPFILES = $(addsuffix .d,$(addprefix $(DEPDIR)/,$(basename $(TESTSRCS) $(MANUAL))))
DEPDIRS = $(sort $(dir $(DEPFILES)))
CLEANFILES += $(DEPFILES)

$(DEPDIRS): | Makefile-distfiles
	$(MKDIR_P) $@

$(DEPFILES): | $(DEPDIRS)

include $(wildcard $(DEPFILES))

docker-dep-check: .docker_deps .aur_deps
	$(DIFF) -u $^

.docker_deps: hooks/build
	$(SHELL) -c 'source <($(SED) -nE "/^(RUN|'"'"')/{s/.*=/echo /;p}" $<)' | \
		$(TR) ' ' '\n' | \
		$(SORT) > $@

CLEANFILES += .docker_deps

.aur_deps:
	$(CURL) -Ls 'https://aur.archlinux.org/cgit/aur.git/plain/.SRCINFO?h=sile-git' | \
		$(SED) -nE '/\bdepends =/{s/.*= //;p}' | \
		$(GREP) -vxE '(lua-.*|.*\.so|git|glibc)' | \
		$(SORT) > $@

CLEANFILES += .aur_deps

docker-ghcr-to-hub:
	$(DOCKER) pull $(GHCR_REGISTRY)/$(GHCR_REPO):$(DOCKER_TAG)
	$(DOCKER) tag  $(GHCR_REGISTRY)/$(GHCR_REPO):$(DOCKER_TAG) $(DOCKER_REGISTRY)/$(DOCKER_REPO):$(DOCKER_TAG)
	$(docker_push)

docker-test-dist: export DOCKERFILE = tests/Dockerfile-dist
docker-test-dist: export DOCKER_TAG = dist-test
docker-test-dist:
	./hooks/build $(VERSION)

vendored-crates: sile-vendored-crates-$(VERSION).tar.zst

sile-vendored-crates-$(VERSION).tar.zst: cargo-vendor
	tardir=vendor && $(am__tar) | $(ZSTD) -c $${ZSTD_CLEVEL-$${ZSTD_OPT--19}} > $@

gource.webm:
	$(MKDIR_P) /tmp/gravatars
	magick documentation/sile-logo.pdf[0] -density 300 -colorspace RGB -negate -resize 50% /tmp/sile-logo.jpg
	$(GIT) log --pretty=format:"%an—%ae" | \
		$(SORT) -u | \
		while IFS=— read name email; do \
			test -f "/tmp/gravatars/$$name.jpg" || \
				$(CURL) -S "https://www.gravatar.com/avatar/$$(echo -n $$email | md5sum | cut -d\  -f1)?d=identicon&s=256" -o "/tmp/gravatars/$$name.jpg" ; \
		done ;\
	gource -a 0.2 -s 0.2 -i 0 --logo /tmp/sile-logo.jpg -b 000000 --max-file-lag 5 --hide filenames --date-format '%Y-%m-%d' --user-image-dir /tmp/gravatars --user-filter simoncozens --key -1920x1080 -o - | \
		ffmpeg -y -r 60 -f image2pipe -vcodec ppm -i - -vcodec libvpx -b 10000K $@

else !DEVELOPER_MODE

$(PHONY_DEVELOPER_TARGETS):
	@: $(error "Please reconfigure using --enable-developer-mode to use developer tooling")

endif !DEVELOPER_MODE
